diff options
| -rw-r--r-- | .gitmodules | 6 | ||||
| -rw-r--r-- | Makefile | 13 | ||||
| -rw-r--r-- | README.md | 31 | ||||
| -rw-r--r-- | gsctool.c | 516 | ||||
| -rw-r--r-- | gsctool.h | 96 | ||||
| -rw-r--r-- | gsctool/config.ini | 3 | ||||
| -rw-r--r-- | gsctool/spawn_raygun/main.gsc | 31 | ||||
| -rw-r--r-- | images/demo.png | bin | 0 -> 51826 bytes | |||
| m--------- | lib/cdl86 | 0 | ||||
| m--------- | lib/miniz | 0 | ||||
| -rw-r--r-- | offsets.h | 17 | 
11 files changed, 713 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..182a0cb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "lib/cdl86"] +	path = lib/cdl86 +	url = https://github.com/lunarjournal/cdl86.git +[submodule "lib/miniz"] +	path = lib/miniz +	url = https://github.com/lunarjournal/miniz.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..598cda1 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CC=tcc.exe +CFLAGS=-m32 -shared -lmsvcrt +CFILES_GSC=gsctool.c lib/cdl86/cdl.c lib/miniz/miniz.c + +all: gsctool + +gsctool: gsctool.c +	$(CC) $(CFLAGS) $(CFILES_GSC) -o gsctool.dll + +.PHONY: clean +clean: +	rm -f gsctool.dll + diff --git a/README.md b/README.md new file mode 100644 index 0000000..9f21000 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# gsctool
 +
 +Simple GSC loader and dumper for `Call Of Duty: Black Ops 1 (T5) (Microsoft Windows)`.
 +
 +To load the demo GSC plugin copy `gsctool` to the games root directory.
 +
 +This demo will give you a raygun on spawn in zombie mode (solo). 
 +
 +* GSC dumps are written to `gsctool/cache`
 +
 +This project is intended to be a starting point for more advanced mods.
 +
 +# Instructions
 +
 +Run: `git submodule update --init --recursive` to clone submodules.
 +
 +Then build project using the provided makefile in Windows Subsystem for Linux
 +and inject resulting DLL.
 +
 +The compiler used for this project is `tcc`.
 +
 +Note: You can modify and reload GSC scripts while Black Ops is running by quiting
 +the level and starting it again.
 +
 +
 +
 +# Libraries
 +
 +* [cdl86](https://github.com/lunarjournal/cdl86) detour library
 +* [miniz](https://github.com/lunarjournal/miniz) zlib library
 +
 diff --git a/gsctool.c b/gsctool.c new file mode 100644 index 0000000..9c8c39c --- /dev/null +++ b/gsctool.c @@ -0,0 +1,516 @@ +/**
 + * Written by: Dylan Muller
 + * Copyright (c) 2023
 + */
 +
 +/* Global includes */
 +#include <Windows.h>
 +#include <direct.h>
 +
 +/* Local includes */
 +#include "offsets.h"
 +#include "gsctool.h"
 +#include "lib/miniz/miniz.h"
 +#include "lib/cdl86/cdl.h"
 +
 +/* Local definitions */
 +#define SIZE_BUF 1024 * 1024
 +#define SIZE_PATH 256
 +
 +/* Assign function pointers */
 +/* Load GSC script */
 +Scr_LoadScript_t Scr_LoadScript = (Scr_LoadScript_t)T5_Scr_LoadScript;
 +/* Get VM function handle */
 +Scr_GetFunctionHandle_t Scr_GetFunctionHandle = (Scr_GetFunctionHandle_t)T5_Scr_GetFunctionHandle;
 +/* Create new execution thread */
 +Scr_ExecThread_t Scr_ExecThread = (Scr_ExecThread_t)T5_Scr_ExecThread;
 +/* Free execution thread */
 +Scr_FreeThread_t Scr_FreeThread = (Scr_FreeThread_t)T5_Scr_FreeThread;
 +/* Execute on level startup */
 +Scr_LoadGameType_t Scr_LoadGameType = (Scr_LoadGameType_t)T5_Scr_LoadGameType;
 +/* Dvar lookup */
 +Dvar_FindVar_t Dvar_FindVar = (Dvar_FindVar_t)T5_Dvar_FindVar;
 +/* Link asset/rawfile. Should be called before Scr_LoadScript */
 +DB_LinkXAssetEntry_t DB_LinkXAssetEntry = (DB_LinkXAssetEntry_t)T5_DB_LinkXAssetEntry;
 +/* Hotfix to prevent crash */
 +Assign_Hotfix_t Assign_Hotfix = (Assign_Hotfix_t)T5_Assign_Hotfix;
 +/* Remove anti-debug timer */
 +Thread_Timer_t Thread_Timer = (Thread_Timer_t)T5_Thread_Timer;
 +
 +/* Global variables */
 +int* init_trigger = (int*)T5_init_trigger;
 +uint8_t mod_dir[SIZE_PATH];
 +uint8_t mod_dir_relative[SIZE_PATH];
 +uint8_t entry_file[SIZE_PATH];
 +int func_handle = 0;
 +
 +/* Strip extension from name */
 +void strip_ext(uint8_t* fname)
 +{
 +	uint8_t* end = fname + strlen(fname);
 +
 +	while (end > fname && *end != '.')
 +	{
 +		--end;
 +	}
 +
 +	if (end > fname)
 +	{
 +		*end = '\0';
 +	}
 +}
 +
 +/* Print banner */
 +void print_banner()
 +{
 +	printf("[info] gsctool init\n");
 +	printf("[info] gsc dumper and loader\n");
 +}
 +
 +/* Print mod info (config.ini) */
 +void print_mod_info(uint8_t* dir, uint8_t* entry)
 +{
 +	printf("mod.dir=%s\n", dir);
 +	printf("mod.entry=%s\n", entry);
 +}
 +
 +/* Print example config.ini */
 +void print_mod_example()
 +{
 +	printf("example config.ini:\n");
 +	printf("mod.dir=example\n");
 +	printf("mod.entry=main.gsc\n");
 +}
 +
 +/* Check if file exists */
 +BOOL FileExists(LPCSTR szPath)
 +{
 +	DWORD attrib = GetFileAttributesA(szPath);
 +	return (attrib != INVALID_FILE_ATTRIBUTES
 +		&& !(attrib & FILE_ATTRIBUTE_DIRECTORY));
 +}
 +
 +/* Check if directory */
 +BOOL DirectoryExists(LPCSTR szPath)
 +{
 +	DWORD attrib = GetFileAttributesA(szPath);
 +	return (attrib != INVALID_FILE_ATTRIBUTES
 +		&& (attrib & FILE_ATTRIBUTE_DIRECTORY));
 +}
 +
 +/* Hotfix to prevent crash when exiting gamemode. */
 +int* __cdecl Assign_Hotfix_hk(
 +	int* a1, int* a2
 +)
 +{
 +	if (a1 != NULL && a2 != NULL)
 +	{
 +		return Assign_Hotfix(a1, a2);
 +	}
 +	else
 +	{
 +		return 0;
 +	}
 +}
 +
 +/* AC bypass to allow detouring. */
 +void __cdecl Thread_Timer_hk(
 +	uint8_t a1, int a2,
 +	int a3, int a4
 +)
 +{
 +	return;
 +}
 +
 +/* Load our custom GSC script */
 +int32_t __cdecl Scr_LoadScript_hk(
 +	int32_t scriptInstance,
 +	const uint8_t* scriptName
 +)
 +{
 +	uint8_t script_file[SIZE_PATH];
 +	uint8_t mapname_buffer[SIZE_PATH];
 +	uint8_t* mapname = NULL;
 +	FILE* gsc_script = NULL;;
 +	uint8_t* source_buffer = 0x0;
 +	uint8_t* data_buffer = 0x0;
 +	uint8_t* compress_buffer = 0x0;
 +	uint32_t gsc_size = 0x0;
 +	int cmp_status = 0x0;
 +	RawFileData* file_data = 0x0;
 +	mz_ulong gsc_compress_size = 0x0;
 +	mz_ulong data_size = 0;
 +	XAsset entry = { 0 };
 +	int ret = 0x0;
 +	int result = 0x0;
 +
 +	result = Scr_LoadScript(scriptInstance, scriptName);
 +
 +	/* Get current map name */
 +	mapname = Dvar_FindVar("mapname");
 +	sprintf(mapname_buffer, "maps/%s", mapname);
 +
 +	/* Are we loading script for current map? */
 +	if (strcmp(mapname_buffer, scriptName) == 0 
 +	     && strcmp(mapname_buffer, "maps/frontend") != 0)
 +	{
 +		/* Load GSC script to inject */
 +		FILE* gsc_script = fopen(entry_file, "rb");
 +		if (gsc_script)
 +		{
 +			/* Get GSC script size in bytes */
 +			fseek(gsc_script, 0, SEEK_END);
 +			gsc_size = ftell(gsc_script);
 +			fseek(gsc_script, 0, SEEK_SET);
 +
 +			/* Allocate GSC script text buffer */
 +			source_buffer = (uint8_t*)malloc((sizeof(uint8_t) * gsc_size) + 1);
 +
 +			/* Return if we cannot allocate memory */
 +			if (!source_buffer)
 +			{
 +				printf("[info] failed allocating memory\n");
 +				fclose(gsc_script);
 +				return result;
 +			}
 +			/* Read GSC script into buffer */
 +			fread(source_buffer, sizeof(uint8_t), gsc_size, gsc_script);
 +
 +			/* Don't forget null terminator! */
 +			source_buffer[gsc_size] = 0;
 +
 +			/* Estimate compression bounds for allocator */
 +			gsc_compress_size = mz_compressBound(gsc_size + 1);
 +			/* Allocate data buffer for compressed data */
 +			data_buffer = (uint8_t*)malloc((sizeof(uint8_t) * gsc_compress_size)
 +				                           + sizeof(RawFileData));
 +
 +			/* Return if we cannot allocate memory */
 +			if (!data_buffer)
 +			{
 +				printf("[info] failed allocating memory\n");
 +				fclose(gsc_script);
 +				free(source_buffer);
 +				return result;
 +			}
 +			compress_buffer = (uint8_t*)(data_buffer + sizeof(RawFileData));
 +			/* Apply zlib compression */
 +			cmp_status = mz_compress((uint8_t*)compress_buffer, &gsc_compress_size, 
 +				                     (uint8_t*)source_buffer, gsc_size + 1);
 +			/* Now free source buffer */
 +			free(source_buffer);
 +
 +			if (cmp_status == MZ_OK){
 +				/* If compression succeeded set compression and deflate lengths */
 +				if (data_buffer) {
 +					file_data = (RawFileData*)data_buffer;
 +					file_data->compressedSize = gsc_compress_size;
 +					file_data->deflatedSize = gsc_size + 1;
 +				}
 +
 +				/* Calculate total payload size */
 +				data_size = gsc_compress_size + sizeof(RawFileData);
 +				/* Set entry type */
 +				entry.type = ASSET_TYPE_RAWFILE;
 +				entry.header.rawFile = (RawFile*)malloc(sizeof(RawFile));
 +
 +				/* Return if we cannot allocate memory */
 +				if (!entry.header.rawFile)
 +				{
 +					printf("[info] failed allocating memory\n");
 +					fclose(gsc_script);
 +					free(data_buffer);
 +					return result;
 +				}
 +				entry.header.rawFile->buffer = data_buffer;
 +				entry.header.rawFile->len = data_size;
 +				entry.header.rawFile->name = (uint8_t*)entry_file;
 +	
 +				printf("[info] linking asset %s\n", entry_file);
 +				/* Link script asset before compilation */
 +				DB_LinkXAssetEntry(&entry, 0);
 +	
 +				sprintf(script_file, "%s", entry_file);
 +				strip_ext(script_file);
 +				/* Compile GSC script */
 +				ret = Scr_LoadScript(0, script_file);
 +
 +				/* Free asset */
 +				free(data_buffer);
 +				free(entry.header.rawFile);
 +	
 +				printf("[info] retreiving function handle\n");
 +				/* Get main VM entry point */
 +				func_handle = Scr_GetFunctionHandle(0, script_file, "main");
 +				if (func_handle)
 +				{
 +					printf("[info] main @ %p\n", (uint8_t*)func_handle);
 +				}
 +				else
 +				{
 +					printf("[error] failed to retreive main handle\n");
 +				}
 +				fclose(gsc_script);
 +			}
 +		}
 +	}
 +
 +	return result;
 +}
 +
 +/* Execute function handle at correct timing */
 +void __cdecl Scr_LoadGameType_hk()
 +{
 +	Scr_LoadGameType();
 +	uint8_t* mapname = Dvar_FindVar("mapname");
 +
 +	if (strcmp(mapname, "frontend") == 0)
 +	{
 +		printf("[info] loaded main menu\n");
 +	}
 +	else if (func_handle > 0)
 +	{
 +		printf("[info] executing main @ %p\n", (uint8_t*)func_handle);
 +		int16_t handle = Scr_ExecThread(0, func_handle, 0);
 +		Scr_FreeThread(handle, 0);
 +	}
 +	return;
 +}
 +
 +/* Recursively make directory */
 +void __cdecl mkdir_recursive(uint8_t* path)
 +{
 +	int i = 0x0;
 +	uint8_t* buffer = NULL;
 +
 +	if (path != NULL) {
 +		buffer = (uint8_t*)malloc(SIZE_PATH);
 +		if (!buffer)
 +		{
 +			printf("[info] failed allocating memory\n");
 +			return;
 +		}
 +		int len_path = strlen(path);
 +		for (i = 0; i < len_path; i++)
 +		{
 +			if (path[i] == '/')
 +			{
 +				strncpy(buffer, path, i);
 +				buffer[i] = 0;
 +				mkdir(buffer);
 +			}
 +		}
 +		free(buffer);
 +	}
 +
 +
 +}
 +
 +/* Dump GSC and CSC scripts as they are loaded */
 +XAssetEntryPoolEntry* __cdecl DB_LinkXAssetEntry_hk(
 +	XAsset* newEntry,
 +	int32_t allowOverride
 +)
 +{
 +	struct RawFile* rawfile = newEntry->header.rawFile;
 +	const uint8_t* data = 0x0;
 +	uint8_t* buffer = (uint8_t*)malloc(SIZE_BUF);
 +	uint8_t* path = (uint8_t*)malloc(SIZE_PATH);
 +	uint8_t* script_name = 0x0;
 +	z_stream stream;
 +	int status = 0x0;
 +	int inflate_len = 0x0;
 +	FILE* inflated_script = 0x0;
 +
 +	if (!buffer || !path)
 +	{
 +		printf("[info] failed allocating memory\n");
 +		return DB_LinkXAssetEntry(newEntry, allowOverride);
 +	}
 +
 +	if (newEntry->type == ASSET_TYPE_RAWFILE && rawfile!= NULL)
 +	{
 +		script_name = rawfile->name;
 +
 +
 +		/* Check for GSC or CSC script */
 +		if (strstr(script_name, ".gsc") != NULL
 +			|| strstr(script_name, ".csc") != NULL)
 +		{
 +			memset(&stream, 0, sizeof(stream));
 +			/* Apply file data offset */
 +			data = (const uint8_t*)(rawfile->buffer + FILE_OFFSET);
 +
 +			/* Setup decompression stream */
 +			stream.next_in = data;
 +			stream.avail_in = rawfile->len - FILE_OFFSET;
 +			stream.next_out = buffer;
 +			stream.avail_out = SIZE_BUF;
 +
 +			/* Initialize inflate with stream */
 +			inflateInit(&stream);
 +			/* Decompress script... */
 +			status = inflate(&stream, Z_SYNC_FLUSH);
 +			inflateEnd(&stream);
 +			inflate_len = stream.total_out;
 +
 +			/* On decompression success create dir and write file */
 +			if (status == Z_STREAM_END || status == Z_OK)
 +			{
 +				sprintf(path, "%s/%s", PATH_DUMP, script_name);
 +				printf("[cache] %s\n", path);
 +				mkdir_recursive(path);
 +				inflated_script = fopen(path, "wb");
 +				if (inflated_script)
 +				{
 +					fwrite(buffer, sizeof(uint8_t), inflate_len, inflated_script);
 +					fclose(inflated_script);
 +				}
 +			}
 +
 +		}
 +	}
 +	free(buffer);
 +	free(path);
 +
 +	return DB_LinkXAssetEntry(newEntry, allowOverride);
 +}
 +
 +/* Main thread to execute once injected. */
 +DWORD WINAPI init_thread(LPVOID lpParam)
 +{
 +	FILE* dependency_list = NULL;
 +	FILE* config_file = NULL;
 +	uint8_t* mod_dir_rel = (uint8_t*)malloc(SIZE_PATH);
 +	uint8_t* entry_file_rel = (uint8_t*)malloc(SIZE_PATH);
 +
 +	/* Wait until all threads have started. */
 +	while (! *init_trigger) { Sleep(10); }
 +
 +	/* Allocate console and redirect std io. */
 +	AllocConsole();
 +	freopen("CONOUT$", "w", stdout);
 +	freopen("CONIN$", "r", stdin);
 +
 +	if (!mod_dir_rel || !entry_file_rel)
 +	{
 +		printf("[info] failed allocating memory\n");
 +		return EXIT_FAILURE;
 +	}
 +	/* Print startup banner */
 +	print_banner();
 +
 +	mkdir(PATH_ROOT);
 +
 +	config_file = fopen(PATH_ROOT "/config.ini", "r");
 +	if (!config_file)
 +	{
 +		printf("[error] %s", "Error opening config.ini\n");
 +		return EXIT_SUCCESS;
 +	}
 +
 +	printf("[info] loaded config.ini\n");
 +
 +	if (fscanf(config_file, "mod.dir=%s\n", mod_dir_rel) != 1)
 +	{
 +		printf("[error] %s\n", "failed reading 'mod.dir' key in config file\n");
 +		print_mod_example();
 +		return EXIT_SUCCESS;
 +	}
 +
 +	if (fscanf(config_file, "mod.entry=%s\n", entry_file_rel) != 1)
 +	{
 +		printf("[error] %s\n", "failed reading 'mod.entry' from config file\n");
 +		print_mod_example();
 +		return EXIT_SUCCESS;
 +	}
 +
 +	fclose(config_file);
 +
 +	printf("[info] mod info\n");
 +	print_mod_info(mod_dir_rel, entry_file_rel);
 +
 +	sprintf(mod_dir, "%s/%s", PATH_ROOT, mod_dir_rel);
 +	sprintf(mod_dir_relative, "%s", mod_dir_rel);
 +
 +	printf("[info] searching for mod directory...\n");
 +	printf("%s\n", mod_dir);
 +	if (!DirectoryExists(mod_dir))
 +	{
 +		printf("[error] %s\n", "mod directory does not exist");
 +		return EXIT_SUCCESS;
 +	}
 +
 +	printf("[info] mod directory found\n");
 +	sprintf(entry_file, "%s/%s", mod_dir, entry_file_rel);
 +
 +	free(entry_file_rel);
 +	free(mod_dir_rel);
 +
 +	printf("[info] searching for mod entry...\n");
 +
 +	if (!FileExists(entry_file))
 +	{
 +		printf("[error] %s\n", "mod entry file does not exist, check config!");
 +		return EXIT_SUCCESS;
 +	}
 +
 +	printf("[info] mod entry found\n");
 +	printf("[info] applying detours...\n");
 +
 +	/* Subroutine patches. */
 +	struct cdl_jmp_patch thread_timer =
 +		cdl_jmp_attach((void**)&Thread_Timer, Thread_Timer_hk);
 +	struct cdl_jmp_patch assign_hotfix =
 +		cdl_jmp_attach((void**)&Assign_Hotfix, Assign_Hotfix_hk);
 +	struct cdl_jmp_patch link_asset =
 +		cdl_jmp_attach((void**)&DB_LinkXAssetEntry, DB_LinkXAssetEntry_hk);
 +	struct cdl_jmp_patch load_script =
 +		cdl_jmp_attach((void**)&Scr_LoadScript, Scr_LoadScript_hk);
 +	struct cdl_jmp_patch load_game_type =
 +		cdl_jmp_attach((void**)&Scr_LoadGameType, Scr_LoadGameType_hk);
 +
 +	/* Print debug info */
 +	printf("[info] Thread_Timer() detour info\n");
 +	cdl_jmp_dbg(&thread_timer);
 +	printf("[info] Assign_Hotfix() detour info\n");
 +	cdl_jmp_dbg(&assign_hotfix);
 +	printf("[info] DB_LinkXAssetEntry() detour info\n");
 +	cdl_jmp_dbg(&link_asset);
 +	printf("[info] Scr_LoadScript() detour info\n");
 +	cdl_jmp_dbg(&load_script);
 +	printf("[info] Scr_LoadGameType() detour info\n");
 +	cdl_jmp_dbg(&load_game_type);
 +
 +	/* Flush std io */
 +	while (1) 
 +	{ 
 +		flushall();
 +		Sleep(200); 
 +	}
 +
 +	return EXIT_SUCCESS;
 +
 +}
 +
 +/* Main entry point */
 +BOOL APIENTRY DllMain(
 +	HMODULE hModule,
 +	DWORD  ul_reason_for_call,
 +	LPVOID lpReserved
 +)
 +{
 +	switch (ul_reason_for_call)
 +	{
 +	case DLL_PROCESS_ATTACH:
 +		CreateThread((LPSECURITY_ATTRIBUTES)0, 0, (LPTHREAD_START_ROUTINE)init_thread,
 +		             (LPVOID)hModule, 0, (LPDWORD)NULL);
 +		break;
 +	case DLL_THREAD_ATTACH:
 +	case DLL_THREAD_DETACH:
 +	case DLL_PROCESS_DETACH:
 +		break;
 +	}
 +	return TRUE;
 +}
 +
 diff --git a/gsctool.h b/gsctool.h new file mode 100644 index 0000000..8737bc6 --- /dev/null +++ b/gsctool.h @@ -0,0 +1,96 @@ +#ifndef _MAIN_H
 +#define _MAIN_H
 +
 +#include <stdint.h>
 +#include <stdbool.h>
 +
 +#define PATH_ROOT "gsctool"
 +#define PATH_DUMP PATH_ROOT "/cache"
 +#define FILE_OFFSET 0x8
 +
 +typedef struct RawFileData
 +{
 +	int32_t deflatedSize;
 +	int32_t compressedSize;
 +} RawFileData;
 +
 +typedef struct RawFile
 +{
 +	uint8_t* name;
 +	int32_t len;
 +	uint8_t* buffer;
 +} RawFile;
 +
 +typedef enum XAssetType
 +{
 +	ASSET_TYPE_RAWFILE = 0x24
 +} XAssetType;
 +
 +typedef union XAssetHeader
 +{
 +	struct RawFile* rawFile;
 +	void* data;
 +} XAssetHeader;
 +
 +typedef struct XAsset
 +{
 +	enum XAssetType type;
 +	union XAssetHeader header;
 +} XAsset;
 +
 +typedef struct XAssetEntry
 +{
 +	XAsset asset;
 +} XAssetEntry;
 +
 +typedef union XAssetEntryPoolEntry
 +{
 +	struct XAssetEntry entry;
 +	union XAssetEntryPoolEntry* next;
 +} XAssetEntryPoolEntry;
 +
 +typedef int32_t (__cdecl* Scr_LoadScript_t)(
 +	int32_t scriptInstance,
 +	const uint8_t* scriptName
 +);
 +
 +typedef int32_t (__cdecl* Scr_GetFunctionHandle_t)(
 +	int32_t scriptInstance,
 +	const uint8_t* scriptName,
 +	const uint8_t* functioName
 +);
 +
 +typedef uint16_t (__cdecl* Scr_ExecThread_t)(
 +	int32_t scriptInstance,
 +	int32_t handle,
 +	int32_t paramCount
 +);
 +
 +typedef uint16_t (__cdecl* Scr_FreeThread_t)(
 +	uint16_t handle,
 +	int scriptInstance
 +);
 +
 +typedef XAssetEntryPoolEntry* (__cdecl* DB_LinkXAssetEntry_t)(
 +	XAsset* newEntry,
 +	int32_t allowOverride
 +);
 +
 +typedef uint8_t* (__cdecl* Dvar_FindVar_t)(
 +	uint8_t* variable
 +);
 +
 +typedef int* (__cdecl* Assign_Hotfix_t)(
 +	int* a1, int* a2
 +);
 +
 +typedef void (__cdecl* Thread_Timer_t)(
 +	uint8_t a1, int a2,
 +	int a3, int a4
 +);
 +
 +typedef void (__cdecl* Scr_LoadGameType_t)(
 +	void
 +);
 +
 +#endif
 diff --git a/gsctool/config.ini b/gsctool/config.ini new file mode 100644 index 0000000..b29db9c --- /dev/null +++ b/gsctool/config.ini @@ -0,0 +1,3 @@ +mod.dir=spawn_raygun +mod.entry=main.gsc + diff --git a/gsctool/spawn_raygun/main.gsc b/gsctool/spawn_raygun/main.gsc new file mode 100644 index 0000000..b1fef79 --- /dev/null +++ b/gsctool/spawn_raygun/main.gsc @@ -0,0 +1,31 @@ +#include maps\_utility;
 +#include common_scripts\utility;
 +#include maps\_hud_util;
 +
 +main()
 +{
 +    level thread onPlayerConnect();
 +}
 +
 +onPlayerConnect()
 +{
 +    for(;;)
 +    {
 +        level waittill("connected", player);
 +        player thread onPlayerSpawned();
 +    }
 +}
 +
 +onPlayerSpawned()
 +{
 +    self endon("disconnect");
 +    for(;;)
 +    {
 +        self waittill("spawned_player");
 +        self iprintln("Spawned raygun!");
 +        self GiveWeapon("ray_gun_zm");
 +        self SetWeaponAmmoStock("ray_gun_zm", 1000);
 +        self SwitchToWeapon("ray_gun_zm");
 + 
 +    }
 +}
 diff --git a/images/demo.png b/images/demo.png Binary files differnew file mode 100644 index 0000000..a70522b --- /dev/null +++ b/images/demo.png diff --git a/lib/cdl86 b/lib/cdl86 new file mode 160000 +Subproject a3655fbccb17dbada53201bbcb22d641f7e5610 diff --git a/lib/miniz b/lib/miniz new file mode 160000 +Subproject e55522aa315852f79f24483e7f296c77e25f825 diff --git a/offsets.h b/offsets.h new file mode 100644 index 0000000..0683701 --- /dev/null +++ b/offsets.h @@ -0,0 +1,17 @@ +#ifndef _OFFSETS_H
 +#define _OFFSETS_H
 +
 +/* Offsets for Call of Duty: Black Ops 1 (Win32) */
 +/* Found using IDA */
 +#define T5_Scr_LoadScript 0x00661AF0
 +#define T5_Scr_GetFunctionHandle 0x004E3470
 +#define T5_DB_LinkXAssetEntry 0x007A2F10
 +#define T5_Scr_ExecThread 0x005598E0
 +#define T5_Scr_FreeThread 0x005DE2C0
 +#define T5_Scr_LoadGameType 0x004B7F80
 +#define T5_Dvar_FindVar 0x0057FF80
 +#define T5_Assign_Hotfix  0x007A4800
 +#define T5_init_trigger 0x00C793B0
 +#define T5_Thread_Timer 0x004C06E0
 +
 +#endif
  | 
