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
(Created page with "Author: Leon Used Tools: MS Visual Studio 2003 Description: A self written UCC for Republic Commando, still in development. Used for executing Unreal Commandlets. Githu...")
 
Line 11: Line 11:


<code lang=c++>
<code lang=c++>
test
/*
* 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
* 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")
//#pragma comment(lib, "Engine.lib") //Don't even need that, but just in case...
 
#include <windows.h> //VirtualProtect
 
#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
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, "--------------------%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){
appSprintf(Temp, "Warning : %s", V);
 
V = Temp;
}else if(Event==NAME_Progress){
appSprintf(Temp, "%s", V);
 
std::cout << Temp << '\r';
 
return;
}
 
std::cout << V << '\n';
 
GLog->Serialize(V, Event);
}
 
/*
* Helper class to redirect RC's log output to the system console when launching a server
*/
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,
  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
//===============================================================
PVOID* vtable = *reinterpret_cast<PVOID**>(InWarn);
DWORD dwNull;
 
VirtualProtect(&vtable[0], 4, PAGE_EXECUTE_READWRITE, &dwNull);
 
vtable[0] = FFeedbackContextAnsiSerialize;
//===============================================================
 
//Server stuff...
for(int i = 1; i < __argc; i++){
std::string Temp = ToUpper(__argv[i]);
 
//In case user wants to start a server redirect log to console and return to SWRepublicCommando.exe
//Uses RC server command line syntax, not Engine.ServerCommandlet!!!
if(Temp == "-SERVER"){
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]);
DWORD LoadFlags = 0;
std::string ClassName;
bool bIsUScript = false;
 
if(Token == "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;
bIsUScript = true;
}else{
ClassName = __argv[1];
}
 
UClass* Class = UObject::StaticLoadClass(UCommandlet::StaticClass(), NULL, ClassName.c_str(), NULL, LoadFlags, NULL);
 
if(Class){
UCommandlet* Commandlet = ConstructObject<UCommandlet>(Class);
 
InWarn->Logf("Executing %s\n", Class->GetFullName());
 
Commandlet->InitExecution();
Commandlet->ParseParms(appCmdLine());
 
if(!bIsUScript){
Commandlet->Main(appCmdLine());
}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
 
GLog = Log;
}
}
}else{
std::cout << "Usage: ucc <command> <parameters>\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
}
</code>
</code>

Revision as of 05:27, 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


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
  • 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
  • /
  1. pragma comment(lib, "Core.lib")

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

  1. include <windows.h> //VirtualProtect
  1. include <iostream>
  2. include <cstdlib> //__argc, __argv
  3. include <cctype> //std::toupper
  4. include <string>

// Core and Engine //#include "Engine/inc/Engine.h" //Only need Core...

  1. include "Core/inc/Core.h"

//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, "--------------------%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){ appSprintf(Temp, "Warning : %s", V);

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

std::cout << Temp << '\r';

return; }

std::cout << V << '\n';

GLog->Serialize(V, Event); }

/*

  • Helper class to redirect RC's log output to the system console when launching a server
  • /

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, 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 //=============================================================== PVOID* vtable = *reinterpret_cast<PVOID**>(InWarn); DWORD dwNull;

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

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

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

//In case user wants to start a server redirect log to console and return to SWRepublicCommando.exe //Uses RC server command line syntax, not Engine.ServerCommandlet!!! if(Temp == "-SERVER"){ 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]); DWORD LoadFlags = 0; std::string ClassName; bool bIsUScript = false;

if(Token == "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; bIsUScript = true; }else{ ClassName = __argv[1]; }

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

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

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

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

if(!bIsUScript){ Commandlet->Main(appCmdLine()); }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

GLog = Log; } } }else{ std::cout << "Usage: ucc <command> <parameters>\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 }