If you want to help us maintaining this wiki, check out our discord server: https://discord.gg/3u69jMa 

Difference between revisions of "Republic Commando UCC"

From SWRC Wiki
Jump to navigation Jump to search
Line 26: Line 26:


#pragma comment(lib, "Core.lib")
#pragma comment(lib, "Core.lib")
//#pragma comment(lib, "Engine.lib") //Don't even need that, but just in case...


#include <windows.h> //VirtualProtect
#include <windows.h> //VirtualProtect


#include <iostream>
#include <cstdio> //std::puts
#include <cstdlib> //__argc, __argv
#include <cstdlib> //__argc, __argv
#include <cctype> //std::toupper
#include <cctype> //std::toupper
#include <string>
#include <string>


// Core and Engine
//#include "Engine/inc/Engine.h"
//Only need Core...
#include "Core/inc/Core.h"
#include "Core/inc/Core.h"
FOutputDevice* GFileLog; //OutputDevice that writes to a log file


//Helper function used for case-insensitive string comparisons
//Helper function used for case-insensitive string comparisons
Line 62: Line 60:
return;
return;
}else if(Event==NAME_Heading){
}else if(Event==NAME_Heading){
appSprintf(Temp, "--------------------%s--------------------", V);
appSprintf(Temp, "\n--------------------%s--------------------", V);


V = Temp;
V = Temp;
Line 69: Line 67:


V = Temp;
V = Temp;
}else if(Event==NAME_Error || Event==NAME_Warning || Event==NAME_ExecWarning || Event==NAME_ScriptWarning){
}else if(Event == NAME_Error || Event == NAME_Warning || Event == NAME_ExecWarning || Event == NAME_ScriptWarning || Event == NAME_Critical){
appSprintf(Temp, "Warning : %s", V);
appSprintf(Temp, "%s : %s", *FName(Event), V);


V = Temp;
V = Temp;
}else if(Event==NAME_Progress){
appSprintf(Temp, "%s", V);
std::cout << Temp << '\r';
return;
}
}


std::cout << V << '\n';
std::puts(V);


GLog->Serialize(V, Event);
GFileLog->Serialize(V, Event);
}
}


/*
//Entry point, called by modified SWRepublicCommando.exe
* Helper class to redirect RC's log output to the system console
*/
class FOutputDeviceConsole : public FOutputDevice{
public:
FOutputDeviceConsole(FOutputDevice& FileLog) : FileLog(FileLog){}
 
void Serialize(const TCHAR* V, EName Event){
if(Event != NAME_Title){ //Prevents server from spamming the player count to the console
std::cout << *FName(Event) << ": " << V << '\n';
 
FileLog.Serialize(V, Event);
}
}
 
private:
FOutputDevice& FileLog;
};
 
//Entry point called by modified SWRepublicCommando.exe
__declspec(dllexport) void uccInit(const TCHAR* InPackage, const TCHAR* InCmdLine, FOutputDevice* InLog, FOutputDeviceError* InError,
__declspec(dllexport) void uccInit(const TCHAR* InPackage, const TCHAR* InCmdLine, FOutputDevice* InLog, FOutputDeviceError* InError,
  FFeedbackContext* InWarn, FConfigCache*(*ConfigFactory)(), UBOOL RequireConfig){
  FFeedbackContext* InWarn, FConfigCache*(*ConfigFactory)(), UBOOL RequireConfig){
int ExitVal = EXIT_SUCCESS;
int ExitVal = EXIT_SUCCESS;
std::cout << "=======================================\n"
GFileLog = InLog;
"ucc.exe for Star Wars Republic Commando\n"
"made by Leon0628\n"
"=======================================\n\n";


//Small vtable hack to have InWarn call my own Serialize function
//Small vtable hack to have InWarn call my own Serialize function
Line 124: Line 94:
vtable[0] = FFeedbackContextAnsiSerialize;
vtable[0] = FFeedbackContextAnsiSerialize;
//===============================================================
//===============================================================
InWarn->Log("======================================= \n"
"ucc.exe for Star Wars Republic Commando \n"
"made by Leon0628 \n"
"======================================= \n");


//Server stuff...
//Server stuff...
Line 130: Line 105:
std::string Temp = ToUpper(__argv[i]);
std::string Temp = ToUpper(__argv[i]);


//In case user wants to start a server redirect log to console and return to SWRepublicCommando.exe
//In case user wants to start a server, redirecting log to console and returning to SWRepublicCommando.exe
//Uses RC server command line syntax, not Engine.ServerCommandlet!!!
//Uses RC server command line syntax, not Engine.ServerCommandlet!!!
if(Temp == "-SERVER"){
if(Temp == "-SERVER"){
static FOutputDeviceConsole ConsoleLog(*InLog); //Static here just to have everything together in one place
appInit(InPackage, InCmdLine, InWarn, InError, InWarn, ConfigFactory, RequireConfig);
 
appInit(InPackage, InCmdLine, &ConsoleLog, InError, InWarn, ConfigFactory, RequireConfig);


return;
return;
Line 155: Line 128:


std::string Token = ToUpper(__argv[1]);
std::string Token = ToUpper(__argv[1]);
DWORD LoadFlags = 0;
DWORD LoadFlags = LOAD_NoWarn | LOAD_Quiet;
std::string ClassName;
std::string ClassName;
bool bIsUScript = false;
bool bIsUScript = false;


if(Token == "MAKE" || Token == "EDITOR.MAKECOMMANDLET"){
if(Token == "MAKE" || Token == "EDITOR.MAKE" || Token == "EDITOR.MAKECOMMANDLET"){
LoadFlags |= LOAD_DisallowFiles; //Not sure what this does but the original ucc has it too...
LoadFlags |= LOAD_DisallowFiles; //Not sure what this does but the original ucc has it too...
ClassName = "Editor.MakeCommandlet";
ClassName = "Editor.MakeCommandlet";
}else if(Token == "USCRIPT"){ //When executing commandlets written in UnrealScript, like Core.HelloWorldCommandlet
}else if(Token == "USCRIPT"){ //When executing commandlets written in UnrealScript, like Core.HelloWorldCommandlet
ClassName = __argc > 2 ? __argv[2] : Token;
ClassName = __argc > 2 ? __argv[2] : Token; //If no commandlet was specified, just using "USCRIPT" which will give an error
bIsUScript = true;
bIsUScript = true;
}else{
}else{
Line 170: Line 143:


UClass* Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, ClassName.c_str(), NULL, LoadFlags, NULL);
UClass* Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, ClassName.c_str(), NULL, LoadFlags, NULL);
if(!Class) //If class failed to load appending "Commandlet" and trying again
Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, (ClassName + "Commandlet").c_str(), NULL, LoadFlags, NULL);


if(Class){
if(Class){
Line 179: Line 155:
Commandlet->ParseParms(appCmdLine());
Commandlet->ParseParms(appCmdLine());


if(!bIsUScript){
GLog = InWarn; //Redirecting commandlet output to console
 
if(!bIsUScript)
Commandlet->Main(appCmdLine());
Commandlet->Main(appCmdLine());
}else{
else
FOutputDevice* Log = GLog;
FOutputDeviceConsole ConsoleLog(*GLog); //Redirecting UnrealScript log to console
 
GLog = &ConsoleLog;
 
Commandlet->Main(FString(appCmdLine())); //For non-native commandlets this overload has to be used
Commandlet->Main(FString(appCmdLine())); //For non-native commandlets this overload has to be used


GLog = Log;
GLog = GFileLog;
}
}else{
InWarn->Logf("Commandlet %s not found", ClassName.c_str());
}
}
}else{
}else{
std::cout << "Usage:\n"
InWarn->Log("Usage:\n"
"    ucc PackageName.CommandletName <parameters>\n"
"    ucc PackageName.CommandletName <parameters>   \n"
"OR\n"
"OR\n"
"    ucc uscript PackageName.CommandletName <parameters>\n"
"    ucc uscript PackageName.CommandletName <parameters>\n"
"    for commandlets written in UnrealScript\n";
"    for commandlets written in UnrealScript\n");
}
}



Revision as of 21:41, 21 November 2017

Author: Leon

Used Tools: MS Visual Studio 2003

Description: A self written UCC for Republic Commando, still in development. Used for executing Unreal Commandlets.

Github: here

Latest Build: here


UCC.cpp

/*
*	This code compiles to a dll. In order to use it, SWRepublicCommando.exe needs to be modified so
*	that it calls this dll's 'uccInit' instead of 'appInit' from Core.dll
*	Also it's subsystem should be changed from window to console
*	Everything compiles fine with 'Visual Studio .NET 2003' but it should also work with newer
*	versions (when making some adjustments to the headers)
*	The following settings are needed to compile everything without errors:
*		- Character Set = Not Set
*		- Struct Member Alignment = 4 Bytes
*		- Calling Convention = __fastcall
*/

#pragma comment(lib, "Core.lib")

#include <windows.h>	//VirtualProtect

#include <cstdio>	//std::puts
#include <cstdlib>	//__argc, __argv
#include <cctype>	//std::toupper
#include <string>

#include "Core/inc/Core.h"

FOutputDevice* GFileLog;	//OutputDevice that writes to a log file

//Helper function used for case-insensitive string comparisons
std::string ToUpper(const std::string& s){
	std::string result;

	result.reserve(s.size());

	for(std::size_t i = 0; i < s.size(); i++)
		result += std::toupper(s[i]);

	return result;
}

/*
*	Replacement for Serialize function of 'InWarn', passed to appInit
*	Have to do this vtable hack because creating new FFeedbackContext and passing it results in crash
*/
void __stdcall FFeedbackContextAnsiSerialize(const TCHAR* V, EName Event){
	TCHAR Temp[1024]= "";

	if(Event==NAME_Title){
		return;
	}else if(Event==NAME_Heading){
		appSprintf(Temp, "\n--------------------%s--------------------", V);

		V = Temp;
	}else if(Event==NAME_SubHeading){
		appSprintf(Temp, "%s...", V);

		V = Temp;
	}else if(Event == NAME_Error || Event == NAME_Warning || Event == NAME_ExecWarning || Event == NAME_ScriptWarning || Event == NAME_Critical){
		appSprintf(Temp, "%s : %s", *FName(Event), V);

		V = Temp;
	}

	std::puts(V);

	GFileLog->Serialize(V, Event);
}

//Entry point, called by modified SWRepublicCommando.exe
__declspec(dllexport) void uccInit(const TCHAR* InPackage, const TCHAR* InCmdLine, FOutputDevice* InLog, FOutputDeviceError* InError,
								   FFeedbackContext* InWarn, FConfigCache*(*ConfigFactory)(), UBOOL RequireConfig){
	int ExitVal = EXIT_SUCCESS;
	
	GFileLog = InLog;

	//Small vtable hack to have InWarn call my own Serialize function
	//===============================================================
	PVOID* vtable = *reinterpret_cast<PVOID**>(InWarn);
	DWORD dwNull;

	VirtualProtect(&vtable[0], 4, PAGE_EXECUTE_READWRITE, &dwNull);

	vtable[0] = FFeedbackContextAnsiSerialize;
	//===============================================================

	InWarn->Log("======================================= \n"
				"ucc.exe for Star Wars Republic Commando \n"
				"made by Leon0628 \n"
				"======================================= \n");

	//Server stuff...

	for(int i = 1; i < __argc; i++){
		std::string Temp = ToUpper(__argv[i]);

		//In case user wants to start a server, redirecting log to console and returning to SWRepublicCommando.exe
		//Uses RC server command line syntax, not Engine.ServerCommandlet!!!
		if(Temp == "-SERVER"){
			appInit(InPackage, InCmdLine, InWarn, InError, InWarn, ConfigFactory, RequireConfig);

			return;
		}
	}
	
	//Actual UCC stuff...

	try{
		GIsStarted = 1;
		GIsGuarded = 1;

		appInit(InPackage, InCmdLine, InLog, InError, InWarn, ConfigFactory, 1);
		UObject::SetLanguage("int");

		if(__argc > 1){
			//Initializing global state
			GIsUCC = GIsClient = GIsServer = GIsEditor = GIsScriptable = GLazyLoad = 1;

			std::string Token = ToUpper(__argv[1]);
			DWORD LoadFlags = LOAD_NoWarn | LOAD_Quiet;
			std::string ClassName;
			bool bIsUScript = false;

			if(Token == "MAKE" || Token == "EDITOR.MAKE" || Token == "EDITOR.MAKECOMMANDLET"){
				LoadFlags |= LOAD_DisallowFiles;	//Not sure what this does but the original ucc has it too...
				ClassName = "Editor.MakeCommandlet";
			}else if(Token == "USCRIPT"){	//When executing commandlets written in UnrealScript, like Core.HelloWorldCommandlet
				ClassName = __argc > 2 ? __argv[2] : Token;	//If no commandlet was specified, just using "USCRIPT" which will give an error
				bIsUScript = true;
			}else{
				ClassName = __argv[1];
			}

			UClass* Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, ClassName.c_str(), NULL, LoadFlags, NULL);

			if(!Class)	//If class failed to load appending "Commandlet" and trying again
				Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, (ClassName + "Commandlet").c_str(), NULL, LoadFlags, NULL);

			if(Class){
				UCommandlet* Commandlet = ConstructObject<UCommandlet>(Class);

				InWarn->Logf("Executing %s\n", Class->GetFullName());

				Commandlet->InitExecution();
				Commandlet->ParseParms(appCmdLine());

				GLog = InWarn;	//Redirecting commandlet output to console

				if(!bIsUScript)
					Commandlet->Main(appCmdLine());
				else
					Commandlet->Main(FString(appCmdLine()));	//For non-native commandlets this overload has to be used

				GLog = GFileLog;
			}else{
				InWarn->Logf("Commandlet %s not found", ClassName.c_str());
			}
		}else{
			InWarn->Log("Usage:\n"
						"    ucc PackageName.CommandletName <parameters>    \n"
						"OR\n"
						"    ucc uscript PackageName.CommandletName <parameters>\n"
						"    for commandlets written in UnrealScript\n");
		}

		appPreExit();
		GIsGuarded = 0;
	}catch(...){
		ExitVal = EXIT_FAILURE;
		GIsGuarded = 0;
		InError->HandleError();
	}

	appExit();
	std::exit(ExitVal);	//Needed in order to prevent this function from returning to SWRepublicCommando.exe
}