diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0cdd33e --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +config.mk + +*.o + +*.dll +!cncnet5.dll diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..110d298 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +-include config.mk + +OUTPUT = rename.dll +CFLAGS = -std=c99 -Iinc/ +DLL_LDFLAGS = -mdll -Wl,--enable-stdcall-fixup + +OBJS = src/rename.o src/ares.o + +CC ?= gcc +STRIP ?= strip +RM ?= rm + +.PHONY: default +default: $(OUTPUT) + +.PHONY: all +all: rename.dll + +$(OUTPUT): $(OBJS) + $(CC) $(CFLAGS) $(DLL_LDFLAGS) -o $@ $^ -lmsvcrt + $(STRIP) $@ +clean: + $(RM) $(OUTPUT) $(OBJS) diff --git a/README.md b/README.md new file mode 100644 index 0000000..d0ed845 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +CnCNet for Ares +================================================================================ + +Spawns Yuri's Revenge and connects players through cncnet5. This project should be used in conjunction with the [xna-cncnet-client](https://github.com/CnCNet/xna-cncnet-client) and [Ares](https://launchpad.net/ares/+download) + +### Usage + - Place cncnet5.dll in you mod folder with the xna-cncncet-client and the Ares files. + - Optionally place rename.dll in the mod directory. + - Syringe from Ares will automatically detect and include cncnet5.dll. + - Configure xna-cncnet-client to execute Syringe.exe rather than game(md).exe + +### Building + - Download cncnet-for-ares + - Edit src/rename.c, change the strings for the Gane name and file locations. Do not exceed the size of the existing strings in the game. + - Download [win-builds](https://downloads.cncnet.org/win-builds-for-patching.zip) and install it by double clicking win-install.bat + - In the cncnet5-for-ares run build.cmd by double clicking on it. Read the output window any errors you may introduced. + - You should now have rename.dll. Now you can rename rename.dll to something appropriate for your mod (the name will not stop it from being loaded by Syringe). + + +### Caveats + - Source code for cncnet5.dll is not public, only the cncnet5.dll file is provided here. + - You do not need to use the command line -SPAWN argument. + - You may need to edit build.cmd if win-builds did not get installed into C:\win-builds-patch-32\ diff --git a/build.cmd b/build.cmd new file mode 100644 index 0000000..0ab6632 --- /dev/null +++ b/build.cmd @@ -0,0 +1,9 @@ +@echo off +REM +REM cnc-patch environment config +REM +set PATH=C:\win-builds-patch-32\bin +gmake clean +pause +gmake all +pause diff --git a/cncnet5.dll b/cncnet5.dll new file mode 100755 index 0000000..7ab39c0 Binary files /dev/null and b/cncnet5.dll differ diff --git a/inc/patch.h b/inc/patch.h new file mode 100644 index 0000000..8051d15 --- /dev/null +++ b/inc/patch.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013, 2014, 2020 Toni Spets + * Daniel Keeton Jr + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define CLEAR(start, value, end) \ + __asm ( \ + ".section .patch,\"d0\";" \ + ".long " #start ";" \ + ".long " #end "-" #start ";" \ + ".fill " #end "-" #start ", 1, " #value ";" \ + ) + +#define LJMP(src, dst) \ + __asm ( \ + ".section .patch,\"d0\";" \ + ".long " #src ";" \ + ".long 5;" \ + ".byte 0xE9;" \ + ".long " #dst "-" #src " - 5;" \ + ) + +#define CALL(src, dst) \ + __asm ( \ + ".section .patch,\"d0\";" \ + ".long " #src ";" \ + ".long 5;" \ + ".byte 0xE8;" \ + ".long " #dst "-" #src " - 5;" \ + ) + +#define SETDWORD(dst, value) \ + __asm ( \ + ".section .patch,\"d0\";" \ + ".long " #dst ";" \ + ".long 4;" \ + ".long " #value ";" \ + ) + +#define SETSTRING(dst, value) \ + __asm ( \ + ".section .patch,\"d0\";" \ + ".long " #dst ";" \ + ".long (_memcpy_end_" #dst \ + " - _memcpy_start_" #dst ");" \ + "_memcpy_start_" #dst ":;" \ + ".asciz " #value ";" \ + "_memcpy_end_" #dst ":;" \ + ) diff --git a/src/ares.c b/src/ares.c new file mode 100644 index 0000000..97f155e --- /dev/null +++ b/src/ares.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013, 2014 Toni Spets + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +// this tricks syringe to load this dll +__asm ( + ".section .syhks00,\"d8\";" // AlexB said the alignment has to be 16 bytes + ".long 0;" + ".long 0;" + ".string \"stub\";" +); + +#ifdef WWDEBUG +#define dprintf printf +#else +#define dprintf(fmt, ...) +#endif + +static DWORD get_dword(char **p) +{ + DWORD ret = *(DWORD *)*p; + *p += sizeof(DWORD); + return ret; +} + +BOOL WINAPI __declspec(dllexport) +DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + { + char buf[MAX_PATH + 1] = { 0 }; + GetModuleFileName(NULL, buf, sizeof buf); + + dprintf("Patching CnCNet 5 to %s\n", buf); + + PIMAGE_DOS_HEADER dos_hdr = (void *)hinstDLL; + PIMAGE_NT_HEADERS nt_hdr = (void *)((char *)hinstDLL + dos_hdr->e_lfanew); + + char *patch = NULL; + int patch_len = 0; + + for (int i = 0; i < nt_hdr->FileHeader.NumberOfSections; i++) + { + PIMAGE_SECTION_HEADER sct_hdr = IMAGE_FIRST_SECTION(nt_hdr) + i; + + if (strcmp(".patch", (char *)sct_hdr->Name) == 0) + { + patch = (char *)((char *)hinstDLL + sct_hdr->VirtualAddress); + patch_len = sct_hdr->Misc.VirtualSize; + break; + } + + sct_hdr++; + } + + dprintf("Found .patch @ %p (%d bytes)\n", patch, patch_len); + + if (patch) + { + for (char *p = patch; p < patch + patch_len;) + { + DWORD paddress = get_dword(&p); + if (paddress == 0) break; + + DWORD plength = get_dword(&p); + + dprintf("PATCH %8u bytes -> %8X\n", (unsigned int)plength, (unsigned int)paddress); + DWORD protect = 0; + VirtualProtect((void *)paddress, plength, PAGE_EXECUTE_READWRITE, &protect); + memcpy((void *)paddress, p, plength); + VirtualProtect((void *)paddress, plength, protect, NULL); + + p += plength; + } + } + } + + return TRUE; +} diff --git a/src/rename.c b/src/rename.c new file mode 100644 index 0000000..9527a26 --- /dev/null +++ b/src/rename.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 Daniel Keeton Jr + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "patch.h" + +/* + * Change the following strings + * Do not make your new strings longer than the original strings. + * Use the line of ); as a guide, if they aren't all lined up + * then your new string isn't the correct length. + */ + +SETSTRING( 0x00849F48, "Yuri's Revenge" ); +SETSTRING( 0x0082668C, "EXPANDMD%02d.MIX" ); +SETSTRING( 0x0082621C, "AIMD.INI" ); +SETSTRING( 0x00826254, "ARTMD.INI" ); +SETSTRING( 0x00826198, "BATTLEMD.INI" ); +SETSTRING( 0x008261A8, "BATTLEMD*.INI" ); +SETSTRING( 0x008295E8, "%sMD.INI" ); +SETSTRING( 0x00825DF0, "EVAMD.INI" ); +SETSTRING( 0x00830370, "MAPSELMD.INI" ); +SETSTRING( 0x00839724, "MISSIONMD.INI" ); +SETSTRING( 0x00826260, "RULESMD.INI" ); +SETSTRING( 0x0082626C, "RULEMD*.INI" ); +SETSTRING( 0x00825E50, "SOUNDMD.INI" ); +SETSTRING( 0x0081C24C, "THEMEMD.MIX" ); +SETSTRING( 0x00825D94, "THEMEMD.INI" ); +SETSTRING( 0x00826444, "RA2MD.INI" ); +SETSTRING( 0x00826614, "ELOCAL*.MIX" ); +SETSTRING( 0x00826620, "ECACHE*.MIX" ); +SETSTRING( 0x0081C284, "MULTIMD.MIX" ); +SETSTRING( 0x00826780, "MULTIMD.MIX" ); +SETSTRING( 0x0081C2EC, "MAPSMD%02d.MIX" ); +SETSTRING( 0x0082679C, "MAPSMD*.MIX" ); +SETSTRING( 0x0081C210, "MOVMD%02d.MIX" ); +SETSTRING( 0x008266A0, "MIXFILES\\MOVMD03.MIX" ); +SETSTRING( 0x008266B8, "MOVMD03.MIX" ); +SETSTRING( 0x008266C4, "MIXFILES\\MOVMD*.MIX" ); +SETSTRING( 0x008266D8, "MIXFILES\\MOVMD01.MIX" ); +SETSTRING( 0x008266F0, "MOVMD01.MIX" ); +SETSTRING( 0x00826748, "MOVMD*.MIX" );