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
(38 intermediate revisions by 2 users not shown)
Line 3: Line 3:
Used Tools: [[MS Visual Studio 2003]]
Used Tools: [[MS Visual Studio 2003]]


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


Github: [https://github.com/Leon280698/Republic-Commando-UCC here]
Github: [https://github.com/Leon280698/CT here]
 
Latest Build: [http://www.moddb.com/games/star-wars-republic-commando/downloads/star-wars-republic-commando-ucc-exe here]




==UCC.cpp==
==UCC.cpp==


<syntaxhighlight lang="c++" line>
<source lang="cpp" line">
/*
/*
* This code compiles to a dll. In order to use it, SWRepublicCommando.exe needs to be modified so
* This is a custom UCC.exe for Star Wars Republic Commando since the game shipped without one.
* that it calls this dll's 'uccInit' instead of 'appInit' from Core.dll
* Everything compiles fine with Visual Studio .NET 2003 which is being used to achieve maximum compatibility
* Everything compiles fine with 'Visual Studio .NET 2003' but it should also work with newer
* since it was also used to compile RC
* versions (when making some adjustments to the headers)
* The following settings are required in order to compile everything without errors:
* The following settings are needed to compile everything without errors:
* - Character Set = Not Set //Important as RC does not use unicode
* - Character Set = Not Set
* - Struct Member Alignment = 4 Bytes //Probably not necessary, but just in case...
* - Struct Member Alignment = 4 Bytes
* - Calling Convention = __fastcall //RC uses __fastcall as default calling convention
* - Calling Convention = __fastcall
*/
*/


#pragma comment(lib, "Core.lib")
#include "../../Core/Inc/Core.h"
//#pragma comment(lib, "Engine.lib") //Don't even need that, but just in case...
#include "../../Core/Inc/FOutputDeviceFile.h"
 
#include "../../Core/Inc/FOutputDeviceWindowsError.h"
#include <windows.h> //VirtualProtect
#include "../../Core/Inc/FFeedbackContextAnsi.h"
 
#include "../../Core/Inc/FConfigCacheIni.h"
#include <iostream>
#include <cstdlib> //__argc, __argv
#include <cctype> //std::toupper
#include <string>
 
// Core and Engine
//#include "Engine/inc/Engine.h"
//Only need Core...
#include "Core/inc/Core.h"


//Helper function used for case-insensitive string comparisons
void UServerCommandletMain(); //Defined in ServerCommandlet.cpp
std::string ToUpper(const std::string& s){
std::string result;


result.reserve(s.size());
//Output devices


for(std::size_t i = 0; i < s.size(); i++)
FOutputDeviceFile Log;
result += std::toupper(s[i]);
FOutputDeviceWindowsError Error;
FFeedbackContextAnsi Warn;


return result;
void ShowBanner(){
Warn.Log("=======================================");
Warn.Log("ucc.exe for Star Wars Republic Commando");
Warn.Log("made by Leon0628");
Warn.Log("=======================================\n");
}
}


/*
int main(int argc, char** argv){
* Replacement for Serialize function of 'InWarn', passed to appInit
GIsStarted = 1;
* 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){
try{
return;
GIsGuarded = 1;
}else if(Event==NAME_Heading){
appSprintf(Temp, "--------------------%s--------------------", V);


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


V = Temp;
for(int i = 1; i < argc; i++)
}else if(Event==NAME_Error || Event==NAME_Warning || Event==NAME_ExecWarning || Event==NAME_ScriptWarning){
CmdLine += FString(argv[i]) + " ";
appSprintf(Temp, "Warning : %s", V);


V = Temp;
appInit("SWRepublicCommando", *CmdLine, &Log, &Error, &Warn, FConfigCacheIni::Factory, 1);
}else if(Event==NAME_Progress){
UObject::SetLanguage("int");
appSprintf(Temp, "%s", V);


std::cout << Temp << '\r';
if(argc > 1){
//Initializing global state
GIsUCC = GIsClient = GIsServer = GIsEditor = GIsScriptable = GLazyLoad = 1;


return;
FString ClassName = argv[1];
}
TArray<FRegistryObjectInfo> List;


std::cout << V << '\n';
UObject::GetRegistryObjects(List, UClass::StaticClass(), UCommandlet::StaticClass(), 0); //Loading list of commandlets declared in .int files


GLog->Serialize(V, Event);
for(int i = 0; i < List.Num(); i++){ //Looking Token up in list and autocompleting class name if found
}
FString Str = List[i].Object;


/*
if(ClassName == Str || ClassName + "Commandlet" == Str){ //Checking against "PackageName.ClassName (+ Commandlet)"
* Helper class to redirect RC's log output to the system console when launching a server
ClassName = List[i].Object;
*/
class FOutputDeviceConsole : public FOutputDevice{
public:
FOutputDeviceConsole(FOutputDevice& FileLog) : FileLog(FileLog){}


void Serialize(const TCHAR* V, EName Event){
break;
if(Event != NAME_Title){ //Prevents server from spamming the player count to the console
}
std::cout << *FName(Event) << ": " << V << '\n';


FileLog.Serialize(V, Event);
while(Str.InStr(".") >= 0) //Removing package name so that only class name remains
}
Str = Str.Mid(Str.InStr(".") + 1);
}


private:
if(ClassName == Str || ClassName + "Commandlet" == Str){ //Checking against "ClassName (+ Commandlet)" and adding "PackageName"
FOutputDevice& FileLog;
ClassName = List[i].Object;
};


//Entry point called by modified SWRepublicCommando.exe
break;
__declspec(dllexport) void uccInit(const TCHAR* InPackage, const TCHAR* InCmdLine, FOutputDevice* InLog, FOutputDeviceError* InError,
}
  FFeedbackContext* InWarn, FConfigCache*(*ConfigFactory)(), UBOOL RequireConfig){
}
int ExitVal = EXIT_SUCCESS;
std::cout << "=======================================\n"
"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
DWORD LoadFlags = LOAD_NoWarn | LOAD_Quiet;
//===============================================================
PVOID* vtable = *reinterpret_cast<PVOID**>(InWarn);
DWORD dwNull;


VirtualProtect(&vtable[0], 4, PAGE_EXECUTE_READWRITE, &dwNull);
if(ClassName == "Editor.MakeCommandlet")
LoadFlags |= LOAD_DisallowFiles;


vtable[0] = FFeedbackContextAnsiSerialize;
UClass* Class = LoadClass<UCommandlet>(NULL, *ClassName, NULL, LoadFlags, NULL);
//===============================================================


//Server stuff...
if(!Class) //If class failed to load appending "Commandlet" and trying again
for(int i = 1; i < __argc; i++){
Class = LoadClass<UCommandlet>(NULL, *(ClassName + "Commandlet"), NULL, LoadFlags, NULL);
std::string Temp = ToUpper(__argv[i]);


//In case user wants to start a server redirect log to console and return to SWRepublicCommando.exe
if(Class){
//Uses RC server command line syntax, not Engine.ServerCommandlet!!!
UCommandlet* Commandlet = ConstructObject<UCommandlet>(Class);
if(Temp == "-SERVER"){
UCommandlet* Default = static_cast<UCommandlet*>(Class->GetDefaultObject());
static FOutputDeviceConsole ConsoleLog(*InLog); //Static here just to have everything together in one place
 
appInit(InPackage, InCmdLine, &ConsoleLog, 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]);
if(Default->ShowBanner)
DWORD LoadFlags = 0;
ShowBanner();
std::string ClassName;
bool bIsUScript = false;


if(Token == "MAKE" || Token == "EDITOR.MAKECOMMANDLET"){
Warn.Logf("Executing %s\n", Class->GetFullName());
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;
bIsUScript = true;
}else{
ClassName = __argv[1];
}


UClass* Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, ClassName.c_str(), NULL, LoadFlags, NULL);
GIsClient = Default->IsClient;
GIsEditor = Default->IsEditor;
GIsServer = Default->IsServer;
GLazyLoad = Default->LazyLoad;


if(Class){
//Contains only the command-line options that are passed to the commandlet
UCommandlet* Commandlet = ConstructObject<UCommandlet>(Class);
FString CommandletCmdLine;


InWarn->Logf("Executing %s\n", Class->GetFullName());
for(int i = 2; i < argc; i++)
CommandletCmdLine += FString(argv[i]) + " ";


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


if(!bIsUScript){
if(Default->LogToStdout){ //Redirecting commandlet output to console
Commandlet->Main(appCmdLine());
Warn.AuxOut = GLog;
}else{
GLog = &Warn;
FOutputDevice* Log = GLog;
}
FOutputDeviceConsole ConsoleLog(*GLog); //Redirecting UnrealScript log to console
if(ClassName == "Engine.ServerCommandlet")
UServerCommandletMain(); //The ServerCommandlet has a special Main function
else
Commandlet->Main(CommandletCmdLine);


GLog = &ConsoleLog;
if(Default->ShowErrorCount)
Warn.Logf("\n%s - %i error(s), %i warning(s)", Warn.ErrorCount == 0 ? "Success" : "Failure", Warn.ErrorCount, Warn.WarningCount);


Commandlet->Main(FString(appCmdLine())); //For non-native commandlets this overload has to be used
if(Default->LogToStdout){
 
Warn.AuxOut = NULL;
GLog = Log;
GLog = &Log;
}
}
}else{
ShowBanner();
Warn.Logf("Commandlet %s not found", argv[1]);
}
}
}else{
}else{
std::cout << "Usage: ucc <command> <parameters>\n";
ShowBanner();
Warn.Log("Usage:");
Warn.Log("    ucc CommandletName <parameters>");
}
}


appPreExit();
//This prevents an infinite loop during garbage collection when there are compile errors with ucc make
//Hopefully only a temporary fix...
if(Warn.ErrorCount == 0)
appPreExit();
 
GIsGuarded = 0;
GIsGuarded = 0;
}catch(...){
}catch(...){
ExitVal = EXIT_FAILURE;
GIsGuarded = 0;
GIsGuarded = 0;
InError->HandleError();
GLog = &Log;
Error.HandleError();
}
}


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

Revision as of 19:19, 4 August 2018

Author: Leon

Used Tools: MS Visual Studio 2003

Description: A self written UCC for Republic Commando. Used for executing Unreal Commandlets.

Github: here

Latest Build: here


UCC.cpp

/*
*	This is a custom UCC.exe for Star Wars Republic Commando since the game shipped without one.
*	Everything compiles fine with Visual Studio .NET 2003 which is being used to achieve maximum compatibility
*	since it was also used to compile RC
*	The following settings are required in order to compile everything without errors:
*		- Character Set = Not Set	//Important as RC does not use unicode
*		- Struct Member Alignment = 4 Bytes	//Probably not necessary, but just in case...
*		- Calling Convention = __fastcall	//RC uses __fastcall as default calling convention
*/

#include "../../Core/Inc/Core.h"
#include "../../Core/Inc/FOutputDeviceFile.h"
#include "../../Core/Inc/FOutputDeviceWindowsError.h"
#include "../../Core/Inc/FFeedbackContextAnsi.h"
#include "../../Core/Inc/FConfigCacheIni.h"

void UServerCommandletMain(); //Defined in ServerCommandlet.cpp

//Output devices

FOutputDeviceFile Log;
FOutputDeviceWindowsError Error;
FFeedbackContextAnsi Warn;

void ShowBanner(){
	Warn.Log("=======================================");
	Warn.Log("ucc.exe for Star Wars Republic Commando");
	Warn.Log("made by Leon0628");
	Warn.Log("=======================================\n");
}

int main(int argc, char** argv){
	GIsStarted = 1;

	try{
		GIsGuarded = 1;

		FString CmdLine;

		for(int i = 1; i < argc; i++)
			CmdLine += FString(argv[i]) + " ";

		appInit("SWRepublicCommando", *CmdLine, &Log, &Error, &Warn, FConfigCacheIni::Factory, 1);
		UObject::SetLanguage("int");

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

			FString ClassName = argv[1];
			TArray<FRegistryObjectInfo> List;

			UObject::GetRegistryObjects(List, UClass::StaticClass(), UCommandlet::StaticClass(), 0); //Loading list of commandlets declared in .int files

			for(int i = 0; i < List.Num(); i++){ //Looking Token up in list and autocompleting class name if found
				FString Str = List[i].Object;

				if(ClassName == Str || ClassName + "Commandlet" == Str){ //Checking against "PackageName.ClassName (+ Commandlet)"
					ClassName = List[i].Object;

					break;
				}

				while(Str.InStr(".") >= 0) //Removing package name so that only class name remains
					Str = Str.Mid(Str.InStr(".") + 1);

				if(ClassName == Str || ClassName + "Commandlet" == Str){ //Checking against "ClassName (+ Commandlet)" and adding "PackageName"
					ClassName = List[i].Object;

					break;
				}
			}

			DWORD LoadFlags = LOAD_NoWarn | LOAD_Quiet;

			if(ClassName == "Editor.MakeCommandlet")
				LoadFlags |= LOAD_DisallowFiles;

			UClass* Class = LoadClass<UCommandlet>(NULL, *ClassName, NULL, LoadFlags, NULL);

			if(!Class) //If class failed to load appending "Commandlet" and trying again
				Class = LoadClass<UCommandlet>(NULL, *(ClassName + "Commandlet"), NULL, LoadFlags, NULL);

			if(Class){
				UCommandlet* Commandlet = ConstructObject<UCommandlet>(Class);
				UCommandlet* Default = static_cast<UCommandlet*>(Class->GetDefaultObject());

				if(Default->ShowBanner)
					ShowBanner();

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

				GIsClient = Default->IsClient;
				GIsEditor = Default->IsEditor;
				GIsServer = Default->IsServer;
				GLazyLoad = Default->LazyLoad;

				//Contains only the command-line options that are passed to the commandlet
				FString CommandletCmdLine;

				for(int i = 2; i < argc; i++)
					CommandletCmdLine += FString(argv[i]) + " ";

				Commandlet->InitExecution();
				Commandlet->ParseParms(*CommandletCmdLine);

				if(Default->LogToStdout){ //Redirecting commandlet output to console
					Warn.AuxOut = GLog;
					GLog = &Warn;
				}
				
				if(ClassName == "Engine.ServerCommandlet")
					UServerCommandletMain(); //The ServerCommandlet has a special Main function
				else
					Commandlet->Main(CommandletCmdLine);

				if(Default->ShowErrorCount)
					Warn.Logf("\n%s - %i error(s), %i warning(s)", Warn.ErrorCount == 0 ? "Success" : "Failure", Warn.ErrorCount, Warn.WarningCount);

				if(Default->LogToStdout){
					Warn.AuxOut = NULL;
					GLog = &Log;
				}
			}else{
				ShowBanner();
				Warn.Logf("Commandlet %s not found", argv[1]);
			}
		}else{
			ShowBanner();
			Warn.Log("Usage:");
			Warn.Log("    ucc CommandletName <parameters>");
		}

		//This prevents an infinite loop during garbage collection when there are compile errors with ucc make
		//Hopefully only a temporary fix...
		if(Warn.ErrorCount == 0)
			appPreExit();

		GIsGuarded = 0;
	}catch(...){
		GIsGuarded = 0;
		GLog = &Log;
		Error.HandleError();
	}

	appExit();
}